package lg.minecraft.plugin.library.config;

import lg.minecraft.plugin.library.LGPlugin;
import lg.minecraft.plugin.library.common.ObserverThread;
import lg.minecraft.plugin.library.item.ItemProperties;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemFlag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

/**
 * @author langle__
 * @version 1.0
 */
@Getter
public class LGBaseConfig extends YamlConfiguration {

    protected final LGPlugin plugin;
    protected final String name;
    protected final String path;
    protected final File file;
    protected final File absoluteFile;
    protected final String resourcePath;

    @Getter
    @Setter
    protected boolean autoReload = false;

    private LGBaseConfig(Builder builder) {
        this.plugin = builder.plugin;
        this.name = builder.name.endsWith(".yml") ? builder.name : builder.name + ".yml";
        this.path = builder.path == null ? plugin.getDataFolder().getPath() : builder.path;

        this.file = new File(this.path, this.name);
        this.absoluteFile = file.getAbsoluteFile();

        this.resourcePath = builder.resourcePath;
    }

    protected LGBaseConfig(LGPlugin plugin, String name) {
        this(new Builder().plugin(plugin).name(name));
    }

    protected LGBaseConfig(LGPlugin plugin, String name, String path) {
        this(new Builder().plugin(plugin).name(name).path(path));
    }

    /**
     * 初始化方法
     * 该方法用于执行对象的初始化操作，包括默认配置的保存、加载以及自动加载功能的添加
     */
    protected void init() {
        this.saveDefault();
        this.load();
        this.addAutoLoad();
    }

    /**
     * 添加自动加载
     * 如果当前对象实现了 AutoLoad 接口，则将当前对象添加为文件监视器的观察者。
     */
    public void addAutoLoad() {
        if (!isAutoReload()) return;
        ObserverThread.INSTANCE.addFileListener(this);
    }

    public void load() {
        if (absoluteFile.exists()) {
            try {
                this.load(absoluteFile);
            } catch (Exception e) {
                plugin.getLogger().severe("无法加载配置文件: " + absoluteFile.getPath());
            }
        } else {
            InputStream input = plugin.getResource(name);
            if (input == null) return;
            try {
                this.load(new InputStreamReader(input, StandardCharsets.UTF_8));
            } catch (Exception e) {
                plugin.getLogger().severe("无法加载配置文件: " + absoluteFile.getPath());
            }
        }
        onLoad();
    }

    protected void onLoad() {}

    public void save() {
        onSave();
        if (!absoluteFile.exists()) {
            this.createDirectory();
        }
        try {
            this.save(absoluteFile);
        } catch (IOException e) {
            plugin.getLogger().severe("无法保存配置文件: " + absoluteFile.getPath());
        }
    }

    public void onSave() {}

    public void asyncSave() {
        Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> this.save());
    }

    public void saveDefault() {
        saveDefault(false);
    }

    public void saveDefault(boolean replace) {
        saveDefault(resourcePath, replace);
    }

    public void saveDefault(String resourcePath, boolean replace) {
        if (!absoluteFile.exists() || replace) {
            plugin.saveResource(resourcePath + file.getName(), replace);
        }
    }

    public void createDirectory() {
        File directory = this.absoluteFile.getParentFile();
        if (!directory.exists() && !directory.mkdirs()) {
            plugin.getLogger().severe("无法创建目录: " + directory.getPath());
        }
    }

    @NotNull
    public ItemProperties getItemProperties(@NotNull String path) {
        ItemProperties properties = new ItemProperties();
        Material material = getMaterial(path + ".material");
        if (material != null) properties.setMaterial(material);
        String name = getString(path + ".name");
        if (name != null) properties.setName(name);
        properties.setLore(getStringList(path + ".lore"));
        for (String string : getStringList(path + ".enchantments")) {
            String[] split = string.split(":", 2);
            if (split.length != 2) {
                plugin.getLogger().warning("无法获取附魔: " + string);
                continue;
            }
            Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(split[0].toLowerCase()));
            if (enchantment == null) {
                plugin.getLogger().warning("无法获取附魔: " + split[0]);
                continue;
            }
            int level = Integer.parseInt(split[1]);
            properties.addEnchant(enchantment, level);
        }
        for (String string : getStringList(path + ".flag")) {
            ItemFlag flag = getItemFlag(string);
            if (flag != null) properties.addItemFlag(flag);
        }
        return properties;
    }

    private ItemFlag getItemFlag(String itemFlag) {
        try {
            return ItemFlag.valueOf(itemFlag.toUpperCase());
        } catch (IllegalArgumentException e) {
            plugin.getLogger().warning("无法获取物品标志: " + itemFlag);
            return null;
        }
    }

    public void setItemProperties(@NotNull String path, @NotNull ItemProperties properties) {
        set(path + ".material", properties.getMaterial().name());
        set(path + ".name", properties.getName());
        set(path + ".lore", properties.getLore());
    }

    @Nullable
    public Material getMaterial(String path) {
        return getMaterial(path, null);
    }

    public Material getMaterial(@NotNull String path, Material def) {
        String string = getString(path);
        if (string == null) return def;
        Material material = Material.matchMaterial(string);
        return material == null ? def : material;
    }

    public void setMaterial(@NotNull String path, @NotNull Material material) {
        set(path, material.name());
    }

    public Location getLocation(@NotNull String path) {
        World world = getWorld(path + ".world", Bukkit.getWorlds().get(0));
        double x = getDouble(path + ".x");
        double y = getDouble(path + ".y");
        double z = getDouble(path + ".z");
        float yaw = getFloat(path + ".yaw");
        float pitch = getFloat(path + ".pitch");
        return new Location(world, x, y, z, yaw, pitch);
    }

    public void setLocation(@NotNull String path, Location location) {
        World world = location.getWorld();
        if (world != null) {
            set(path + ".world", world.getName());
        }
        set(path + ".x", location.getX());
        set(path + ".y", location.getY());
        set(path + ".z", location.getZ());
        set(path + ".yaw", location.getYaw());
        set(path + ".pitch", location.getPitch());
    }

    @Nullable
    public World getWorld(@NotNull String path) {
        return getWorld(path, null);
    }

    public World getWorld(@NotNull String path, World def) {
        String worldName = getString(path);
        if (worldName == null) return def;
        World world = Bukkit.getWorld(worldName);
        return world == null ? def : world;
    }

    public void setWorld(@NotNull String path, @NotNull World world) {
        set(path, world.getName());
    }

    public float getFloat(@NotNull String path) {
        Object def = getDefault(path);
        return getFloat(path, (def instanceof Number) ? toFloat(def) : 0);
    }

    public float getFloat(@NotNull String path, float def) {
        Object val = get(path, def);
        return (val instanceof Number) ? toFloat(val) : def;
    }

    public static float toFloat(@Nullable Object object) {
        if (object instanceof Number) {
            return ((Number) object).floatValue();
        }

        try {
            if (object != null) {
                return Float.parseFloat(object.toString());
            }
        } catch (Exception ignored) {
        }

        return 0;
    }

    public static class Builder {

        private LGPlugin plugin;
        private String name;
        private String path;
        private String resourcePath = "";

        public Builder() {
        }

        public Builder plugin(@NotNull LGPlugin plugin) {
            this.plugin = plugin;
            return this;
        }

        public Builder name(@NotNull String name) {
            this.name = name;
            return this;
        }

        public Builder path(@NotNull String path) {
            this.path = path;
            return this;
        }

        public Builder resourcePath(@NotNull String resourcePath) {
            this.resourcePath = resourcePath;
            return this;
        }

        public LGBaseConfig build() {
            if (plugin == null) throw new IllegalStateException("Plugin cannot be null");
            if (name == null) throw new IllegalStateException("Name cannot be null");
            return new LGBaseConfig(this);
        }

    }

}
